/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Andrey Loskutov <loskutov@gmx.de> - Bug 445538 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 472654 * Patrik Suzzi <psuzzi@gmail.com> - Bug 489250 *******************************************************************************/ package org.eclipse.ui.internal.dialogs.cpd; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.commands.Command; import org.eclipse.core.commands.ParameterizedCommand; import org.eclipse.e4.ui.workbench.renderers.swt.HandledContributionItem; import org.eclipse.jface.bindings.Binding; import org.eclipse.jface.bindings.BindingManager; import org.eclipse.jface.preference.PreferenceDialog; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Tree; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.dialogs.PreferencesUtil; import org.eclipse.ui.internal.WorkbenchMessages; import org.eclipse.ui.internal.WorkbenchWindow; import org.eclipse.ui.internal.dialogs.cpd.CustomizePerspectiveDialog.ActionSet; import org.eclipse.ui.internal.dialogs.cpd.CustomizePerspectiveDialog.DisplayItem; import org.eclipse.ui.internal.dialogs.cpd.CustomizePerspectiveDialog.DynamicContributionItem; import org.eclipse.ui.internal.dialogs.cpd.CustomizePerspectiveDialog.ShortcutItem; import org.eclipse.ui.internal.dialogs.cpd.TreeManager.TreeItem; import org.eclipse.ui.internal.keys.BindingService; import org.eclipse.ui.internal.util.Util; import org.eclipse.ui.keys.IBindingService; /** * A tooltip with useful information based on the type of ContributionItem * the cursor hovers over in a Tree. In addition to the content provided by * the {@link NameAndDescriptionToolTip} this includes action set * information and key binding data. * * @since 3.5 */ class ItemDetailToolTip extends NameAndDescriptionToolTip { private Tree tree; private boolean showActionSet; private boolean showKeyBindings; private ViewerFilter filter; private TreeViewer v; private CustomizePerspectiveDialog dialog; /** * @param dialog * @param tree * The tree for the tooltip to hover over */ ItemDetailToolTip(CustomizePerspectiveDialog dialog, TreeViewer v, Tree tree, boolean showActionSet, boolean showKeyBindings, ViewerFilter filter) { super(tree,NO_RECREATE); this.dialog = dialog; this.tree = tree; this.v = v; this.showActionSet = showActionSet; this.showKeyBindings = showKeyBindings; this.filter = filter; this.setHideOnMouseDown(false); } @Override public Point getLocation(Point tipSize, Event event) { // try to position the tooltip at the bottom of the cell ViewerCell cell = v.getCell(new Point(event.x, event.y)); if( cell != null ) { return tree.toDisplay(event.x,cell.getBounds().y+cell.getBounds().height); } return super.getLocation(tipSize, event); } @Override protected Object getToolTipArea(Event event) { // Ensure that the tooltip is hidden when the cell is left return v.getCell(new Point(event.x, event.y)); } @Override protected void addContent(Composite destination, Object modelElement) { final DisplayItem item = (DisplayItem) modelElement; // Show any relevant action set info if (showActionSet) { String text = null; Image image = null; if(CustomizePerspectiveDialog.isEffectivelyAvailable(item, filter)) { if(item.actionSet != null) { //give information on which command group the item is in final String actionSetName = item.getActionSet().descriptor .getLabel(); text = NLS.bind( WorkbenchMessages.HideItems_itemInActionSet, actionSetName); } } else { //give feedback on why item is unavailable image = dialog.warningImageDescriptor.createImage(); if (item.getChildren().isEmpty() && item.getActionSet() != null) { //i.e. is a leaf final String actionSetName = item.getActionSet(). descriptor.getLabel(); text = NLS.bind( WorkbenchMessages.HideItems_itemInUnavailableActionSet, actionSetName); } else if (item.getChildren().isEmpty() && item.getActionSet() == null && item.getIContributionItem() instanceof HandledContributionItem) { text = WorkbenchMessages.HideItems_itemInUnavailableCommand; } else { //i.e. has children Set<ActionSet> actionGroup = new LinkedHashSet<>(); ItemDetailToolTip.collectDescendantCommandGroups(actionGroup, item, filter); if (actionGroup.size() == 1) { //i.e. only one child ActionSet actionSet = actionGroup. iterator().next(); text = NLS.bind( WorkbenchMessages.HideItems_unavailableChildCommandGroup, actionSet.descriptor.getId(), actionSet.descriptor.getLabel()); } else { //i.e. multiple children String commandGroupList = null; for (ActionSet actionSet : actionGroup) { // For each action set, make a link for it, set // the href to its id String commandGroupLink = MessageFormat.format( "<a href=\"{0}\">{1}</a>", //$NON-NLS-1$ actionSet.descriptor.getId(), actionSet.descriptor.getLabel()); if (commandGroupList == null) { commandGroupList = commandGroupLink; } else { commandGroupList = Util.createList( commandGroupList, commandGroupLink); } } commandGroupList = NLS.bind( "{0}{1}", new Object[] { CustomizePerspectiveDialog.NEW_LINE, commandGroupList }); //$NON-NLS-1$ text = NLS.bind( WorkbenchMessages.HideItems_unavailableChildCommandGroups, commandGroupList); } } } if(text != null) { Link link = createEntryWithLink(destination, image, text); link.addSelectionListener(new SelectionListener() { @Override public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } @Override public void widgetSelected(SelectionEvent e) { ActionSet actionSet = dialog.idToActionSet.get(e.text); if (actionSet == null) { hide(); dialog.showActionSet(item); } else { hide(); dialog.showActionSet(actionSet); } } }); } } // Show key binding info if (showKeyBindings && CustomizePerspectiveDialog.getCommandID(item) != null) { // See if there is a command associated with the command id ICommandService commandService = dialog.window .getService(ICommandService.class); Command command = commandService.getCommand(CustomizePerspectiveDialog.getCommandID(item)); if (command != null && command.isDefined()) { // Find the bindings and list them as a string Binding[] bindings = ItemDetailToolTip.getKeyBindings(dialog.window, item); String keybindings = ItemDetailToolTip.keyBindingsAsString(bindings); String text = null; // Is it possible for this item to be visible? final boolean available = (item.getActionSet() == null) || (item.getActionSet().isActive()); if (bindings.length > 0) { if (available) { text = NLS.bind( WorkbenchMessages.HideItems_keyBindings, keybindings); } else { text = NLS .bind( WorkbenchMessages.HideItems_keyBindingsActionSetUnavailable, keybindings); } } else { if (available) { text = WorkbenchMessages.HideItems_noKeyBindings; } else { text = WorkbenchMessages.HideItems_noKeyBindingsActionSetUnavailable; } } // Construct link to go to the preferences page for key // bindings final Object highlight; if (bindings.length == 0) { Map<String, String> parameters = new HashMap<>(); // If item is a shortcut, need to add a parameter to go // to // the correct item if (item instanceof ShortcutItem) { if (CustomizePerspectiveDialog.isNewWizard(item)) { parameters.put( IWorkbenchCommandConstants.FILE_NEW_PARM_WIZARDID, CustomizePerspectiveDialog.getParamID(item)); } else if (CustomizePerspectiveDialog.isShowPerspective(item)) { parameters .put( IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE_PARM_ID, CustomizePerspectiveDialog.getParamID(item)); } else if (CustomizePerspectiveDialog.isShowView(item)) { parameters.put( IWorkbenchCommandConstants.VIEWS_SHOW_VIEW_PARM_ID, CustomizePerspectiveDialog.getParamID(item)); } } ParameterizedCommand pc = ParameterizedCommand .generateCommand(command, parameters); highlight = pc; } else { highlight = bindings[0]; } Link bindingLink = createEntryWithLink(destination, null, text); bindingLink.addSelectionListener(new SelectionListener() { @Override public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } @Override public void widgetSelected(SelectionEvent e) { PreferenceDialog pdialog = PreferencesUtil.createPreferenceDialogOn(dialog.getShell(), CustomizePerspectiveDialog.KEYS_PREFERENCE_PAGE_ID, new String[0], highlight); hide(); pdialog.open(); } }); } } // Show dynamic menu item info if (item instanceof DynamicContributionItem) { DynamicContributionItem dynamic = ((DynamicContributionItem) item); StringBuffer text = new StringBuffer(); final List<MenuItem> currentItems = dynamic.getCurrentItems(); if (currentItems.size() > 0) { // Create a list of the currently displayed items text.append(WorkbenchMessages.HideItems_dynamicItemList); for (MenuItem menuItem : currentItems) { text.append(CustomizePerspectiveDialog.NEW_LINE).append("- ") //$NON-NLS-1$ .append(menuItem.getText()); } } else { text .append(WorkbenchMessages.HideItems_dynamicItemEmptyList); } createEntry(destination, null, text.toString()); } } @Override protected Object getModelElement(Event event) { org.eclipse.swt.widgets.TreeItem treeItem = tree.getItem(new Point( event.x, event.y)); if (treeItem == null) { return null; } return treeItem.getData(); } /** * @param collection * a collection, into which all command groups (action sets) * which contribute <code>item</code> or its descendants will be * placed * @param item * the item to collect descendants of * @param filter * the filter currently being used */ static void collectDescendantCommandGroups(Collection<ActionSet> collection, DisplayItem item, ViewerFilter filter) { List<TreeItem> children = item.getChildren(); for (TreeItem treeItem : children) { DisplayItem child = (DisplayItem) treeItem; if ((filter == null || filter.select(null, null, child)) && child.getActionSet() != null) { collection.add(child.getActionSet()); } collectDescendantCommandGroups(collection, child, filter); } } /** * @param bindings * @return a String representing the key bindings in <code>bindings</code> */ static String keyBindingsAsString(Binding[] bindings) { String keybindings = null; for (int i = 0; i < bindings.length; i++) { // Unfortunately, bindings may be reported more than once: // check to see if this one has already been recorded. boolean alreadyRecorded = false; for (int j = 0; j < i && !alreadyRecorded; j++) { if (bindings[i].getTriggerSequence().equals( bindings[j].getTriggerSequence())) { alreadyRecorded = true; } } if (!alreadyRecorded) { String keybinding = bindings[i].getTriggerSequence().format(); if (i == 0) { keybindings = keybinding; } else { keybindings = Util.createList(keybindings, keybinding); } } } return keybindings; } /** * Gets the keybindings associated with a ContributionItem. */ static Binding[] getKeyBindings(WorkbenchWindow window, DisplayItem item) { IBindingService bindingService = window.getService(IBindingService.class); if (!(bindingService instanceof BindingService)) { return new Binding[0]; } String id = CustomizePerspectiveDialog.getCommandID(item); String param = CustomizePerspectiveDialog.getParamID(item); BindingManager bindingManager = ((BindingService) bindingService) .getBindingManager(); Collection<?> allBindings = bindingManager .getActiveBindingsDisregardingContextFlat(); List<Binding> foundBindings = new ArrayList<>(2); for (Object name : allBindings) { Binding binding = (Binding) name; if (binding.getParameterizedCommand() == null) { continue; } if (binding.getParameterizedCommand().getId() == null) { continue; } if (binding.getParameterizedCommand().getId().equals(id)) { if (param == null) { // We found it! foundBindings.add(binding); } else { // command parameters are only used in the shortcuts Map<?, ?> m = binding.getParameterizedCommand().getParameterMap(); String key = null; if (CustomizePerspectiveDialog.isNewWizard(item)) { key = IWorkbenchCommandConstants.FILE_NEW_PARM_WIZARDID; } else if (CustomizePerspectiveDialog.isShowView(item)) { key = IWorkbenchCommandConstants.VIEWS_SHOW_VIEW_PARM_ID; } else if (CustomizePerspectiveDialog.isShowPerspective(item)) { key = IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE_PARM_ID; } if (key != null) { if (param.equals(m.get(key))) { foundBindings.add(binding); } } } } } Binding[] bindings = foundBindings .toArray(new Binding[foundBindings.size()]); return bindings; } }